package com.czg.jsonschema2lombokpojo.annotator;

import static org.apache.commons.lang3.StringUtils.*;

import java.util.LinkedHashSet;
import java.util.Set;

import com.czg.jsonschema2lombokpojo.config.GenerationConfig;
import org.apache.commons.lang3.StringUtils;
import com.czg.jsonschema2lombokpojo.rules.FormatRule;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JEnumConstant;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;

/**
 * 使用Jackson 2注解生成的Java类型。X映射注解。
 * @author czg
 */
public class Jackson2Annotator extends AbstractTypeInfoAwareAnnotator {


    public Jackson2Annotator(GenerationConfig generationConfig) {
        super(generationConfig);
    }

    @Override
    public void propertyInclusion(JDefinedClass clazz, JsonNode schema) {
    }

    @Override
    public void propertyField(JFieldVar field, JDefinedClass clazz, String propertyName, JsonNode propertyNode) {
        if (field.type().erasure().equals(field.type().owner().ref(Set.class))) {
            field.annotate(JsonDeserialize.class).param("as", LinkedHashSet.class);
        }

        if (propertyNode.has("javaJsonView")) {
            field.annotate(JsonView.class).param(
                    "value", field.type().owner().ref(propertyNode.get("javaJsonView").asText()));
        }
    }


    @Override
    public void anyGetter(JMethod getter, JDefinedClass clazz) {
        getter.annotate(JsonAnyGetter.class);
    }

    @Override
    public void anySetter(JMethod setter, JDefinedClass clazz) {
        setter.annotate(JsonAnySetter.class);
    }

    @Override
    public void enumCreatorMethod(JDefinedClass _enum, JMethod creatorMethod) {
        creatorMethod.annotate(JsonCreator.class);
    }

    @Override
    public void enumValueMethod(JDefinedClass _enum, JMethod valueMethod) {
        valueMethod.annotate(JsonValue.class);
    }

    @Override
    public void enumConstant(JDefinedClass _enum, JEnumConstant constant, String value) {
    }

    @Override
    public boolean isAdditionalPropertiesSupported() {
        return true;
    }

    @Override
    public void dateField(JFieldVar field, JDefinedClass clazz, JsonNode node) {

        String pattern = null;
        if (node.has("customDatePattern")) {
            pattern = node.get("customDatePattern").asText();
        } else if (node.has("customPattern")) {
            pattern = node.get("customPattern").asText();
        } else if (isNotEmpty(getGenerationConfig().getCustomDatePattern())) {
            pattern = getGenerationConfig().getCustomDatePattern();
        } else if (getGenerationConfig().isFormatDates()) {
            pattern = FormatRule.ISO_8601_DATE_FORMAT;
        }

        if (pattern != null && !field.type().fullName().equals("java.lang.String")) {
            field.annotate(JsonFormat.class).param("shape", JsonFormat.Shape.STRING).param("pattern", pattern);
        }
    }

    @Override
    public void timeField(JFieldVar field, JDefinedClass clazz, JsonNode node) {

        String pattern = null;
        if (node.has("customTimePattern")) {
            pattern = node.get("customTimePattern").asText();
        } else if (node.has("customPattern")) {
            pattern = node.get("customPattern").asText();
        } else if (isNotEmpty(getGenerationConfig().getCustomTimePattern())) {
            pattern = getGenerationConfig().getCustomTimePattern();
        } else if (getGenerationConfig().isFormatDates()) {
            pattern = FormatRule.ISO_8601_TIME_FORMAT;
        }

        if (pattern != null && !field.type().fullName().equals("java.lang.String")) {
            field.annotate(JsonFormat.class).param("shape", JsonFormat.Shape.STRING).param("pattern", pattern);
        }
    }

    @Override
    public void dateTimeField(JFieldVar field, JDefinedClass clazz, JsonNode node) {
        String timezone = node.has("customTimezone") ? node.get("customTimezone").asText() : "UTC";

        String pattern = null;
        if (node.has("customDateTimePattern")) {
            pattern = node.get("customDateTimePattern").asText();
        } else if (node.has("customPattern")) {
            pattern = node.get("customPattern").asText();
        } else if (isNotEmpty(getGenerationConfig().getCustomDateTimePattern())) {
            pattern = getGenerationConfig().getCustomDateTimePattern();
        } else if (getGenerationConfig().isFormatDateTimes()) {
            pattern = FormatRule.ISO_8601_DATETIME_FORMAT;
        }

        if (pattern != null && !field.type().fullName().equals("java.lang.String")) {
            field.annotate(JsonFormat.class).param("shape", JsonFormat.Shape.STRING).param("pattern", pattern).param("timezone", timezone);
        }
    }
    @Override
    protected void addJsonTypeInfoAnnotation(JDefinedClass jclass, String propertyName) {
        JAnnotationUse jsonTypeInfo = jclass.annotate(JsonTypeInfo.class);
        jsonTypeInfo.param("use", JsonTypeInfo.Id.CLASS);
        jsonTypeInfo.param("include", JsonTypeInfo.As.PROPERTY);

        // When not provided it will use default provided by "use" attribute
        if (StringUtils.isNotBlank(propertyName)) {
            jsonTypeInfo.param("property", propertyName);
        }
    }
}
